package com.tsing.cli.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.tsing.cli.common.config.RedisManager;
import com.tsing.cli.system.constant.AuthConstants;
import com.tsing.cli.common.dto.CustomUserDetails;
import com.tsing.cli.common.exception.CustomException;
import com.tsing.cli.common.exception.ResponseCode;
import com.tsing.cli.common.util.RsaUtil;
import com.tsing.cli.system.entity.MenuEntity;
import com.tsing.cli.system.entity.UserEntity;
import com.tsing.cli.system.mapper.UserMapper;
import com.tsing.cli.system.service.IAuthorizationService;
import com.tsing.cli.system.vo.req.LoginReqVO;
import com.tsing.cli.system.vo.resp.LoginRespVO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import net.dreamlu.mica.ip2region.core.Ip2regionSearcher;
import net.dreamlu.mica.ip2region.core.IpInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author TheTsing
 * @since 2022-11-28 15:49
 */
@Service
@RequiredArgsConstructor
public class AuthorizationServiceImpl implements IAuthorizationService {

    @Value("${login.token-expire:7200}")
    private Long tokenExpire;

    @Value("${rsa.private_key}")
    private String privateKey;

    @Value("${login.single-login:true}")
    private Boolean singleLogin;

    private final UserMapper userMapper;

    private final RedisManager redisManager;

    private final Ip2regionSearcher ip2regionSearcher;

    private final AuthenticationManager authenticationManager;

    @Override
    public LoginRespVO login(LoginReqVO reqVO, HttpServletRequest request) {
        try {
            reqVO.setPassword(RsaUtil.decryptByPrivateKey(privateKey, reqVO.getPassword()));
        } catch (Exception exception) {
            throw new CustomException(ResponseCode.PASSWORD_FORMAT_ERROR);
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authentication);
        CustomUserDetails customUserDetails = (CustomUserDetails) authenticate.getPrincipal();
        // 社交登录
        /*CustomUserDetails customUserDetails = userDetailsServiceImpl.loadUserByUsername(reqVO.getUsername());
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities()));*/
        String token = JWTUtil.createToken(Map.of("id", customUserDetails.getId()), String.valueOf(System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));
        String ip = JakartaServletUtil.getClientIP(request);
        IpInfo ipInfo = null;
        try {
            ipInfo = ip2regionSearcher.memorySearch(ip);
        } catch (Exception e) {
        }
        UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
        customUserDetails
                .setIp(ip)
                .setAddress(Objects.isNull(ipInfo) ? "Unknown" : ipInfo.getAddress())
                .setBrowser(Objects.isNull(userAgent) ? "Unknown" : userAgent.getBrowser().toString())
                .setLoginTime(System.currentTimeMillis());
        if (singleLogin) {
            Set<String> keys = redisManager.getByKeyPrefix(AuthConstants.REDIS_TOKEN_PREFIX + customUserDetails.getId() + "_");
            redisManager.delete(keys);
        }
        redisManager.setString(AuthConstants.REDIS_TOKEN_PREFIX + customUserDetails.getId() + "_" + token, customUserDetails, tokenExpire);
        UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                .select(UserEntity::getNickName, UserEntity::getPhone, UserEntity::getEmail, UserEntity::getGender, UserEntity::getBirthday, UserEntity::getAvatar)
                .eq(UserEntity::getId, customUserDetails.getId())
                .one();
        LoginRespVO loginRespVO = BeanUtil.copyProperties(userEntity, LoginRespVO.class);
        loginRespVO.setToken(token);
        return loginRespVO;
    }

    @Override
    public List<Tree<String>> build() {
        CustomUserDetails customUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        List<MenuEntity> menuEntities = userMapper.build(customUserDetails.getId());
        return TreeUtil.build(menuEntities, "0", (treeNode, tree) -> {
            tree.setId(treeNode.getId());
            tree.setParentId(treeNode.getPid());
            tree.setName(treeNode.getName());
            tree.setWeight(treeNode.getWeight());
            tree.putExtra("type", treeNode.getType());
            tree.putExtra("path", treeNode.getPath());
            tree.putExtra("component", treeNode.getComponent());
            tree.putExtra("icon", treeNode.getIcon());
        });
    }

    @Override
    public void logout(HttpServletRequest request) {
        CustomUserDetails customUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        redisManager.delete(AuthConstants.REDIS_TOKEN_PREFIX + customUserDetails.getId() + "_" + request.getHeader(AuthConstants.AUTHENTICATION_HEAD));
    }

}
